<template>
  <div
    class="markdown-editor"
    :class="{ 'editor--fullscreen': isFullscreen }"
    :style="{ height: editorContainerHeight }"
  >
    <!-- svg 图标 -->
    <svg xmlns="http://www.w3.org/2000/svg" style="display: none">
      <!-- 撤销 -->
      <symbol id="icon-undo" viewBox="0 0 1024 1024">
        <path
          d="M296.704 145.28l-196.096 196.096 196.096 196.117333 60.352-60.352L263.893333 384H629.333333a202.666667 202.666667 0 0 1 0 405.333333H362.666667v85.333334h266.666666C788.394667 874.666667 917.333333 745.728 917.333333 586.666667S788.394667 298.666667 629.333333 298.666667H264l93.056-93.056-60.352-60.330667z"
        ></path>
      </symbol>

      <!-- 恢复 -->
      <symbol id="icon-redo" viewBox="0 0 1024 1024">
        <path
          d="M727.296 145.28l196.096 196.096-196.096 196.117333-60.352-60.352L760.106667 384H394.666667a202.666667 202.666667 0 0 0 0 405.333333H661.333333v85.333334H394.666667C235.605333 874.666667 106.666667 745.728 106.666667 586.666667S235.605333 298.666667 394.666667 298.666667h365.333333l-93.056-93.056 60.352-60.330667z"
        ></path>
      </symbol>

      <!-- 标题 -->
      <symbol id="icon-heading" viewBox="0 0 1024 1024">
        <path
          d="M256 213.333333h104.874667v267.093334h324.48V213.333333h104.874666v640h-104.874666v-283.264H360.874667V853.333333H256z"
        ></path>
      </symbol>

      <!-- 加粗 -->
      <symbol id="icon-bold" viewBox="0 0 1024 1024">
        <path
          d="M621.6 485.12c66.08-24.64 106.72-86.4 106.72-162.72 0-121.28-88.96-185.44-257.44-185.44H252v750.08h235.2c178.4 0 284.8-82.56 284.8-220.96 0-94.72-54.72-160.32-150.4-180.96z m60.32 177.92c0 123.2-113.12 149.12-208 149.12h-129.76V528h129.76c138.08 0 208 45.6 208 135.04z m-337.76-208v-243.2h114.56c125.12 0 178.56 36.16 178.56 120.64 0 81.44-61.76 122.72-183.52 122.72z"
        ></path>
      </symbol>

      <!-- 斜体 -->
      <symbol id="icon-italic" viewBox="0 0 1024 1024">
        <path
          d="M768 85.792h-288a32 32 0 0 0 0 64h96.32l-230.336 704H256a32 32 0 0 0 0 64h288a32 32 0 0 0 0-64h-93.728l230.528-704H768a32 32 0 0 0 0-64z"
        ></path>
      </symbol>

      <!-- 删除线 -->
      <symbol id="icon-strikethrough" viewBox="0 0 1024 1024">
        <path
          d="M944.128 449.5872h-275.6608c-20.48-10.6496-42.8544-21.504-67.2256-32.512-70.5536-31.8976-115.6096-54.3744-135.4752-67.2256-27.0336-17.5616-46.3872-35.584-58.368-53.76a81.2544 81.2544 0 0 1-13.2096-45.8752c0-27.1872 12.4928-51.3536 37.376-72.3968 24.832-20.992 58.5728-31.5392 101.12-31.5392 55.04 0 106.4448 20.0192 153.9584 59.5456 47.616 39.7312 77.1072 92.16 89.088 157.696h25.7024l-7.0144-260.7616h-18.6368c-5.0176 24.0128-10.8544 38.8608-17.0496 44.6976-6.3488 5.9904-14.848 9.0112-25.7024 9.0112-11.3152 0-31.232-6.3488-59.392-18.688-28.5184-12.544-52.3776-20.992-72.192-25.7024a330.0864 330.0864 0 0 0-80.128-9.5232c-75.264 0-137.8304 22.016-187.6992 66.048-50.0736 43.9296-74.9056 96.768-74.9056 159.0272 0 36.864 9.0112 70.2464 27.1872 100.608 4.352 7.168 9.216 14.336 14.336 21.1968H153.9584v56.3712h203.8784c2.1504 1.6896 4.1472 3.1744 6.2976 4.7104 34.048 24.6784 89.8048 54.3744 167.2192 89.2416 54.8864 25.1904 93.2352 45.7216 114.7904 61.2352 21.504 15.36 37.6832 31.744 48.6912 48.896 10.8544 17.152 16.384 35.1744 16.384 53.76 0 31.1296-14.0288 58.368-42.0864 81.7152-28.0064 23.3472-65.3824 35.0208-112.2816 35.0208-60.2112 0-116.4288-21.7088-168.96-65.024-52.4288-43.5712-87.9616-106.8032-106.6496-189.952h-23.3472v295.0144h23.3472c7.8336-15.5136 17.5104-27.3408 29.5424-35.0208 11.9808-7.68 23.3472-11.52 34.2016-11.52 13.0048 0 29.4912 3.84 49.8688 11.52 35.84 13.824 65.536 23.1936 89.6 28.3648a368.64 368.64 0 0 0 75.5712 7.4752c85.6064 0 156.16-23.3472 211.4048-70.5536 55.3984-46.8992 82.944-103.1168 82.944-168.4992 0-51.5584-17.0496-97.4336-51.4048-137.472a276.7872 276.7872 0 0 0-42.752-38.912h183.9104V449.5872z"
        ></path>
      </symbol>

      <!-- 链接 -->
      <symbol id="icon-link" viewBox="0 0 24 24">
        <path
          d="M10 13C10.4295 13.5741 10.9775 14.0491 11.6066 14.3929C12.2356 14.7367 12.9315 14.9411 13.6466 14.9923C14.3618 15.0435 15.0796 14.9403 15.7513 14.6897C16.4231 14.4392 17.0331 14.047 17.54 13.54L20.54 10.54C21.4508 9.59695 21.9548 8.33394 21.9434 7.02296C21.932 5.71198 21.4061 4.45791 20.4791 3.53087C19.5521 2.60383 18.298 2.07799 16.987 2.0666C15.676 2.0552 14.413 2.55918 13.47 3.46996L11.75 5.17996"
          fill="none"
        />
        <path
          d="M14 11C13.5705 10.4259 13.0225 9.95085 12.3934 9.60705C11.7644 9.26325 11.0685 9.05889 10.3534 9.00766C9.63821 8.95643 8.92042 9.05963 8.24866 9.31018C7.5769 9.56072 6.96689 9.95296 6.46 10.46L3.46 13.46C2.54922 14.403 2.04524 15.666 2.05663 16.977C2.06803 18.288 2.59387 19.5421 3.52091 20.4691C4.44795 21.3962 5.70202 21.922 7.013 21.9334C8.32398 21.9448 9.58699 21.4408 10.53 20.53L12.24 18.82"
          fill="none"
        />
      </symbol>

      <!-- 图片 -->
      <symbol id="icon-image" viewBox="0 0 24 24">
        <path
          d="M4 16L8.586 11.414C8.96106 11.0391 9.46968 10.8284 10 10.8284C10.5303 10.8284 11.0389 11.0391 11.414 11.414L16 16M14 14L15.586 12.414C15.9611 12.0391 16.4697 11.8284 17 11.8284C17.5303 11.8284 18.0389 12.0391 18.414 12.414L20 14M14 8H14.01M6 20H18C19.1046 20 20 19.1046 20 18V6C20 4.89543 19.1046 4 18 4H6C4.89543 4 4 4.89543 4 6V18C4 19.1046 4.89543 20 6 20Z"
          fill="none"
        />
      </symbol>

      <!-- 代码 -->
      <symbol id="icon-code" viewBox="0 0 24 24">
        <path d="M10 20L14 4M18 8L22 12L18 16M6 16L2 12L6 8" fill="none" />
      </symbol>

      <!-- 无序列表 -->
      <symbol id="icon-unordered-list" viewBox="0 0 1024 1024">
        <path
          d="M132.608 204.288m-66.56 0a66.56 66.56 0 1 0 133.12 0 66.56 66.56 0 1 0-133.12 0Z"
        ></path>
        <path
          d="M962.01728 158.80192l-680.68352 0.3584-0.04096 84.44928 680.7552-0.3584-0.03072-84.44928z"
          cursorshover="true"
        ></path>
        <path
          d="M132.608 512m-66.56 0a66.56 66.56 0 1 0 133.12 0 66.56 66.56 0 1 0-133.12 0Z"
        ></path>
        <path
          d="M281.33376 466.87232l-0.04096 84.44928 680.7552-0.3584-0.03072-84.44928-680.68352 0.3584z"
        ></path>
        <path
          d="M132.608 819.712m-66.56 0a66.56 66.56 0 1 0 133.12 0 66.56 66.56 0 1 0-133.12 0Z"
        ></path>
        <path
          d="M281.33376 775.59808l-0.04096 84.44928 680.7552-0.3584-0.03072-84.44928-680.68352 0.3584z"
        ></path>
      </symbol>

      <!-- 有序列表 -->
      <symbol id="icon-ordered-list" viewBox="0 0 1024 1024">
        <path
          d="M139.58 132.62v199.3h-32.25V171.99c-11.84 10.89-26.73 18.98-44.91 24.27V163.9c8.82-2.25 18.18-6.16 28.11-11.73 9.91-6.14 18.18-12.57 24.8-19.55h24.25zM171.01 434.24c12.14 11.16 18.2 25.41 18.2 43.27 0 17.3-6.64 32.93-19.3 47.16-7.73 8.39-21.5 18.98-40.8 32.11-20.11 13.39-32.23 25.11-36.64 35.16h97v28.75H52.24c0-20.39 6.34-37.95 19.57-53.02 7.18-8.39 22.32-20.66 45.2-36.57 12.66-8.93 21.48-16.48 27-22.34 8.55-9.77 12.95-20.36 12.95-31.55 0-10.89-3.02-18.98-8.55-24.27-5.8-5.3-14.34-7.82-25.64-7.82-12.11 0-21.2 4.18-27.27 12.57-6.07 7.82-9.36 19.82-9.93 35.43H53.35c0.27-22.32 6.61-40.18 19.3-53.86 12.93-14.5 30.02-21.77 51.25-21.77 19 0 34.73 5.59 47.11 16.75zM171.04 729.06c11.86 9.77 17.91 23.16 17.91 40.48 0 21.77-11.02 36.27-32.8 43.55 11.59 3.61 20.68 8.93 26.75 16.18 6.61 7.52 9.91 17.3 9.91 29.02 0 18.43-6.34 33.5-19.02 45.23-13.23 12-30.57 18.14-52.07 18.14-20.39 0-36.93-5.3-49.34-15.91-13.77-11.73-21.48-29.02-23.14-51.36h32.8c0.55 12.84 4.41 22.89 12.11 29.86 6.91 6.43 16 9.77 27.3 9.77 12.39 0 22.32-3.64 29.48-10.61 6.34-6.41 9.64-14.23 9.64-23.7 0-11.45-3.57-19.82-10.18-25.14-6.61-5.57-16.27-8.09-28.93-8.09h-13.8V801.9h13.8c11.57 0 20.39-2.5 26.45-7.52 5.77-5.02 8.82-12.57 8.82-22.34s-2.75-17.02-8-22.05c-5.8-5.02-14.32-7.55-25.61-7.55-11.59 0-20.41 2.8-26.75 8.66-6.61 5.86-10.45 14.8-11.57 26.8H53.1c1.64-20.09 8.82-35.73 22.05-46.89 12.39-11.16 28.39-16.48 47.66-16.48 19.84 0 36.11 4.75 48.23 14.53z"
        ></path>
        <path
          d="M281.3 180.62h708.8v93.09H281.3zM281.3 478.1h708.8v93.09H281.3zM281.3 775.58h708.8v93.09H281.3z"
          cursorshover="true"
        ></path>
      </symbol>

      <!-- 引用 -->
      <symbol id="icon-blockquote" viewBox="0 0 1024 1024">
        <path
          d="M398.108444 796.444444H227.555556a56.888889 56.888889 0 0 1-56.888889-56.888888V514.275556c0-118.954667 30.890667-184.945778 58.481777-220.615112C289.166222 216.177778 391.964444 170.666667 453.859556 170.666667v86.414222c-40.96 12.515556-96.028444 36.238222-123.562667 72.248889-27.534222 36.010667-45.397333 77.027556-45.397333 137.443555h113.777777a56.888889 56.888889 0 0 1 56.888889 57.059556l-0.568889 215.836444a56.888889 56.888889 0 0 1-56.888889 56.775111z m398.222223 0H625.777778a56.888889 56.888889 0 0 1-56.888889-56.888888V514.275556c0-118.954667 30.890667-184.945778 58.481778-220.615112C687.388444 216.177778 790.812444 170.666667 852.650667 170.666667v86.414222c-40.846222 12.515556-96.597333 36.238222-124.131556 72.248889s-45.397333 77.027556-45.397333 137.443555h113.777778a56.888889 56.888889 0 0 1 56.888888 57.059556l-0.568888 215.836444a56.888889 56.888889 0 0 1-56.888889 56.775111z"
        ></path>
      </symbol>

      <!-- 保存 -->
      <symbol id="icon-save" viewBox="0 0 1024 1024">
        <path
          d="M925.248 356.928l-258.176-258.176a64 64 0 0 0-45.248-18.752H144a64 64 0 0 0-64 64v736a64 64 0 0 0 64 64h736a64 64 0 0 0 64-64V402.176a64 64 0 0 0-18.752-45.248zM288 144h192V256H288V144z m448 736H288V736h448v144z m144 0H800V704a32 32 0 0 0-32-32H256a32 32 0 0 0-32 32v176H144v-736H224V288a32 32 0 0 0 32 32h256a32 32 0 0 0 32-32V144h77.824l258.176 258.176V880z"
          cursorshover="true"
        ></path>
      </symbol>

      <!-- 全屏 -->
      <symbol id="icon-fullscreen" viewBox="0 0 1024 1024">
        <path
          d="M641.750109 384.100028l205.227128-204.519-0.704035 115.89966c-0.282433 9.611915 7.489578 18.09103 17.101493 17.808598l12.297071 0c9.611915-0.283456 17.667382-5.936199 17.808598-15.689331l0.565888-172.57752c0-0.14224 0.282433-9.187243 0.282433-9.187243 0.14224-4.804423-0.99056-9.187243-4.100388-12.297071-3.109828-3.109828-7.347339-5.086855-12.297071-4.946662l-8.763594 0.14224c-0.141216 0-0.278339 0-0.420579 0.14224L697.581696 98.166787c-9.611915 0.283456-17.667382 8.200776-17.808598 17.950837l0 12.297071c1.416256 11.44875 10.458189 18.092054 20.070104 17.808598l112.789832 0.283456-204.66124 203.814965c-9.329483 9.329483-9.329483 24.449855 0 33.778314 9.329483 9.470699 24.452925 9.470699 33.782408 0L641.750109 384.100028zM383.095141 576.889893 177.726797 780.705881l0.707105-115.338888c0.283456-9.607822-7.492648-18.086937-17.104563-17.808598l-13.001105 0c-9.611915 0.283456-17.667382 5.937223-17.808598 15.690354l-0.565888 172.718737c0 0.14224-0.282433 9.187243-0.282433 9.187243-0.14224 4.808516 0.99056 9.187243 4.096295 12.297071 3.109828 3.109828 7.351432 5.086855 12.297071 4.946662l8.762571-0.14224c0.14224 0 0.283456 0 0.425695-0.14224l171.873486 0.708128c9.607822-0.283456 17.667382-8.196683 17.808598-17.950837L344.93503 832.575226c-1.415232-11.44875-10.461259-18.092054-20.074198-17.808598L212.069977 814.483172 416.59 610.671277c9.329483-9.329483 9.329483-24.453948 0-33.782408C407.40685 567.41817 392.424624 567.41817 383.095141 576.889893L383.095141 576.889893zM894.047276 835.967486l-0.424672-172.718737c-0.283456-9.612938-8.200776-15.406898-17.809621-15.690354l-12.296047 0c-9.612938-0.278339-17.243733 8.200776-17.105586 17.808598l0.708128 115.903753L641.750109 576.889893c-9.329483-9.329483-24.452925-9.329483-33.782408 0-9.325389 9.328459-9.325389 24.452925 0 33.782408L812.490795 814.483172l-112.789832 0.283456c-9.611915-0.283456-18.515702 6.502088-20.073174 17.808598l0 12.297071c0.282433 9.611915 8.200776 17.667382 17.808598 17.950837l171.166381-0.708128c0.141216 0 0.282433 0.14224 0.424672 0.14224l8.763594 0.14224c4.803399 0.141216 9.187243-1.694595 12.296047-4.946662 3.109828-3.109828 4.238534-7.488555 4.097318-12.297071 0 0-0.14224-9.046027-0.14224-9.187243L894.047276 835.968509zM212.216309 146.506748l112.789832-0.283456c9.607822 0.283456 18.512632-6.502088 20.070104-17.808598L345.076246 116.116601c-0.283456-9.611915-8.196683-17.667382-17.808598-17.950837l-172.011632 0.708128c-0.14224 0-0.283456-0.14224-0.425695-0.14224l-8.761548-0.14224c-4.808516-0.141216-9.187243 1.694595-12.297071 4.946662-3.109828 3.109828-4.242627 7.488555-4.096295 12.297071 0 0 0.282433 9.046027 0.282433 9.187243l0.420579 172.718737c0.14224 9.608845 8.200776 15.406898 17.808598 15.686261l13.005198 0c9.611915 0.282433 17.242709-8.196683 17.10047-17.808598l-0.564865-115.334795 205.231221 203.958228c9.324366 9.329483 24.448832 9.329483 33.777291 0 9.329483-9.329483 9.329483-24.452925 0-33.782408L212.216309 146.506748 212.216309 146.506748zM212.216309 146.506748"
          cursorshover="true"
        ></path>
      </symbol>
    </svg>

    <!-- 工具栏 -->
    <div class="toolbar" ref="toolbar">
      <div class="button-group">
        <div class="tooltip" data-tooltip="撤销">
          <button @click="undo" :disabled="!canUndo">
            <svg class="icon">
              <use xlink:href="#icon-undo" />
            </svg>
          </button>
        </div>
        <div class="tooltip" data-tooltip="恢复">
          <button @click="redo" :disabled="!canRedo">
            <svg class="icon">
              <use xlink:href="#icon-redo" />
            </svg>
          </button>
        </div>
      </div>

      <div class="dropdown">
        <div class="tooltip" data-tooltip="选择标题">
          <button @click.stop="toggleDropdown">
            <svg class="icon">
              <use xlink:href="#icon-heading" />
            </svg>
            <span class="caret"></span>
          </button>
        </div>
        <ul v-if="dropdownVisible" class="dropdown-menu">
          <li v-for="(level, index) in 6" :key="level" @click.stop="insertHeader(level)">
            {{ ['一', '二', '三', '四', '五', '六'][index] }}级标题
          </li>
        </ul>
      </div>

      <div class="tooltip" data-tooltip="加粗">
        <button @click="insertBold">
          <svg class="icon">
            <use xlink:href="#icon-bold" />
          </svg>
        </button>
      </div>

      <div class="tooltip" data-tooltip="斜体">
        <button @click="insertItalic">
          <svg class="icon">
            <use xlink:href="#icon-italic" />
          </svg>
        </button>
      </div>

      <div class="tooltip" data-tooltip="删除线">
        <button @click="insertStrikethrough">
          <svg class="icon">
            <use xlink:href="#icon-strikethrough" />
          </svg>
        </button>
      </div>

      <!-- <div class="tooltip" data-tooltip="链接">
				<button @click="insertLink">
					<svg class="icon">
						<use xlink:href="#icon-link" />
					</svg>
				</button>
			</div>

			<div class="tooltip" data-tooltip="图片">
				<button @click="insertImage">
					<svg class="icon">
						<use xlink:href="#icon-image" />
					</svg>
				</button>
			</div>

			<div class="tooltip" data-tooltip="代码">
				<button @click="insertCode">
					<svg class="icon">
						<use xlink:href="#icon-code" />
					</svg>
				</button>
			</div> -->

      <div class="tooltip" data-tooltip="无序列表">
        <button @click="insertUnorderedList">
          <svg class="icon">
            <use xlink:href="#icon-unordered-list" />
          </svg>
        </button>
      </div>

      <div class="tooltip" data-tooltip="有序列表">
        <button @click="insertOrderedList">
          <svg class="icon">
            <use xlink:href="#icon-ordered-list" />
          </svg>
        </button>
      </div>

      <div class="tooltip" data-tooltip="引用">
        <button @click="insertBlockquote">
          <svg class="icon">
            <use xlink:href="#icon-blockquote" />
          </svg>
        </button>
      </div>

      <div class="tooltip" data-tooltip="保存">
        <button @click="handleSave">
          <svg class="icon">
            <use xlink:href="#icon-save" />
          </svg>
        </button>
      </div>

      <div class="tooltip" data-tooltip="切换全屏">
        <button @click="isFullscreen = !isFullscreen">
          <svg class="icon">
            <use xlink:href="#icon-fullscreen" />
          </svg>
        </button>
      </div>
    </div>

    <!-- 编辑区域 -->
    <div
      contenteditable="true"
      class="editor vuepress-markdown-body"
      ref="editor"
      :style="{ height: editorHeight }"
      :placeholder="props.placeholder"
      @input="onInput"
      @click="saveSelection"
      @keyup="saveSelection"
      @keydown="handleKeyDown"
      @paste="handlePaste"
    ></div>
  </div>
</template>

<script lang="ts" name="MarkdownEditor" setup>
import { ref, watch, onMounted, nextTick, computed, onUnmounted } from 'vue'
import { marked } from 'marked'
import TurndownService from 'turndown'
import DOMPurify from 'dompurify'
import { gfm } from '@guyplusplus/turndown-plugin-gfm'

interface EditHistoryItem {
  html: string
  markdown: string
  selection: {
    range:
      | {
          startContainer: string
          startOffset: number
          endContainer: string
          endOffset: number
        }
      | undefined
    focusNodePath: string | undefined
  }
}

interface PropsType {
  modelValue: string
  height: string | number
  placeholder?: string
}

const props = withDefaults(defineProps<PropsType>(), {
  height: '200px',
  placeholder: '请输入',
})

const emit = defineEmits(['update:modelValue', 'save'])

const toolbar = ref<HTMLElement | null>(null)
const editor = ref<HTMLElement | null>(null)
const htmlContent = ref<string>('')
const lastSelectionRange = ref<Range | null>(null)
const lastFocusNode = ref<Node | null>(null)
const editHistory = ref<EditHistoryItem[]>([])
const historyIndex = ref<number>(-1)
const isFirstInput = ref<boolean>(true) // 标记是否是第一次输入
const isFullscreen = ref<boolean>(false) // 标记是否全屏
const dropdownVisible = ref(false) // 下拉菜单是否显示
const FORBID_TAGS = ['script', 'style', 'iframe', 'object', 'embed', 'form'] //禁止的标签列表

const toolbarHeight = ref<number>(37)

const turndownService = new TurndownService({
  headingStyle: 'atx', // 使用 # 语法表示标题
  hr: '---', // 使用 --- 表示水平线
  bulletListMarker: '-', // 使用 - 表示无序列表
  codeBlockStyle: 'fenced', // 使用 ``` 表示代码块
  emDelimiter: '*', // 使用 * 表示斜体
  strongDelimiter: '**', // 使用 ** 表示加粗
  linkStyle: 'inlined', // 使用 [text](url) 表示链接
  linkReferenceStyle: 'full', // 使用 [text][id] 和 [id]: url 表示链接
})

const editorContainerHeight = computed(() => {
  return isFullscreen.value
    ? '100vh'
    : props.height === 'number'
      ? `${props.height}px`
      : props.height
})

const editorHeight = computed(() => {
  const height = editorContainerHeight.value
  return `calc(${height} - ${toolbarHeight.value}px)`
})

// 初始化 marked 和 turndown
const renderer = new marked.Renderer()
// console.log('renderer', renderer)
// renderer.text = (token) => {
// 	console.log('text', token)
// 	return `<p>${token.text}</p>`; // 强制生成 <p>
// };
marked.setOptions({
  gfm: true, // 启用 GitHub Flavored Markdown
  breaks: true, // 将换行符转换为 <br>
  pedantic: false, // 禁用非标准的 GFM 扩展
  renderer,
})

turndownService.use(gfm)
turndownService.addRule('保留表格样式', {
  filter: ['th', 'td'],
  replacement: function (content: string, node: Node) {
    const element = node as HTMLElement
    // 关键点：保留子元素的 Markdown 转换结果
    return (
      '|' + turndownService.turndown(element.innerHTML) + (element.nextElementSibling ? '' : '|')
    ) // 使用 turndownService 转换子元素
  },
})
turndownService.addRule('escapeHtml', {
  filter: FORBID_TAGS as unknown as keyof HTMLElementTagNameMap,
  replacement: () => '',
})

// 自定义规则：换行符
turndownService.addRule('breaks', {
  filter: 'br',
  replacement: () => '\n',
})

// 自定义规则：删除线
turndownService.addRule('strikethrough', {
  filter: ['del', 's'],
  replacement: (content: string) => '~~' + content + '~~',
})

// 自定义规则：表格
// turndownService.addRule("table", {
// 	filter: "table",
// 	replacement: (_content, table: HTMLElement) => {
// 		let header = '';
// 		let separator = '';
// 		let body: string[] = [];

// 		if (table == null) return '';

// 		if (table.querySelector("thead")) {
// 			header = `|${Array.from(table.querySelectorAll("thead tr th"))!.map((th) => th.textContent).join('|')}|`;

// 			separator = `|${Array.from(table.querySelectorAll("thead tr th"))!.map(() => '------').join("|")}|`;

// 			body = Array.from(table.querySelectorAll("tbody tr"))!.map((tr: Element) => {
// 				return `|${Array.from(tr.querySelectorAll("td"))!.map((td) => td.textContent).join("|")}|`;
// 			});
// 		} else {
// 			const rows = Array.from(table.querySelectorAll("tr"))!; // 获取所有行
// 			header = `|${(rows[0].querySelectorAll("th").length
// 				? Array.from(rows[0].querySelectorAll("th")).map((th) => th.textContent)
// 				: Array.from(rows[0].querySelectorAll("td")).map((td) => td.textContent)).join('|')}|`;

// 			separator = `|${(rows[0].querySelectorAll("th").length
// 				? Array.from(rows[0].querySelectorAll("th")).map(() => '------')
// 				: Array.from(rows[0].querySelectorAll("td")).map(() => '------')).join('|')}|`;

// 			body = rows.slice(1).map((tr: Element) => {
// 				return `|${Array.from(tr.querySelectorAll("td")).map((td) => td.textContent).join("|")}|`;
// 			})
// 		}
// 		return `${header}\n${separator}\n${body.join("\n")}\n`;
// 	},
// });

/**
 * 判断最后聚焦节点的父节点是否是某个元素
 */
const haveParentOfLastFocusNode = (fn: (node: Node) => boolean) => {
  const selection = window.getSelection()
  let parentNode = selection?.focusNode
  while (parentNode) {
    if (fn(parentNode)) {
      return true
    }
    parentNode = parentNode.parentNode
  }
  return false
}

/**
 * 查找最后聚焦节点是否是某个元素并返回该节点，冒泡查找
 * @param fn 判断函数，返回 true 表示匹配成功
 */
const bubbleFindNodeByFocusNode = (fn: (node: Node) => boolean) => {
  const selection = window.getSelection()
  let parentNode = selection?.focusNode
  let targetNode: Node | null = null
  while (parentNode) {
    if (fn(parentNode)) {
      targetNode = parentNode
      break
    }
    parentNode = parentNode.parentNode
  }
  return targetNode
}

/**
 * 判断元素是否被完全选中
 * @param element 需要判断的元素
 */
const isContentFullySelected = (element: HTMLElement): boolean => {
  const selection = window.getSelection()
  if (!selection || selection.rangeCount === 0) return false

  const userRange = selection.getRangeAt(0)
  const fullRange = document.createRange()

  // 创建一个完整的范围，包含整个元素的内容
  fullRange.selectNodeContents(element)

  // 比较用户选择的范围和完整范围
  const isStartSame = userRange.compareBoundaryPoints(Range.START_TO_START, fullRange) === 0
  const isEndSame = userRange.compareBoundaryPoints(Range.END_TO_END, fullRange) === 0

  // 如果范围完全匹配，则认为内容被完全选中
  if (isStartSame && isEndSame) {
    return true
  }

  // 特殊情况：处理空内容或占位符（如 <br>）
  if (element.childNodes.length === 1 && element.firstChild?.nodeName === 'BR') {
    return userRange.startContainer === element && userRange.endOffset === 1
  }

  // 处理嵌套结构和跨元素选择
  const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null)
  const textNodes: Text[] = []
  let node: Node | null = walker.nextNode()

  while (node) {
    textNodes.push(node as Text)
    node = walker.nextNode()
  }

  // 获取所有文本节点的内容
  const allText = textNodes.map((node) => node.nodeValue).join('')

  // 获取选区内的文本内容
  const selectedText = userRange.toString()

  // 比较选区文本和所有文本是否一致
  return allText.trim() === selectedText.trim()
}

// 缩进列表
const indentList = (li: HTMLElement) => {
  const prevLi = li?.previousElementSibling

  if (prevLi) {
    // 创建子列表容器
    let sublist = prevLi.querySelector('ul, ol')
    if (!sublist) {
      sublist = document.createElement('ul')
      prevLi.appendChild(sublist)
    }

    // 移动当前项到子列表
    sublist.appendChild(li)
  }
}

// 减少缩进
const unindentList = (li: HTMLElement) => {
  const parentUl = li.parentElement
  const parentLi = parentUl?.parentElement

  if (parentLi?.nodeName === 'LI') {
    // 获取后面的兄弟节点列表
    const nextSiblingsList: Element[] = []
    let nextSibling = li.nextElementSibling
    while (nextSibling) {
      nextSiblingsList.push(nextSibling)
      nextSibling = nextSibling.nextElementSibling
    }

    if (nextSiblingsList.length > 0) {
      let childUl = li.querySelector('ul, ol')
      if (!childUl) {
        // 没有子级列表，创建一个
        childUl = document.createElement('ul')
        li.appendChild(childUl)
      }
      for (const sibling of nextSiblingsList) {
        // 将后面的兄弟节点移动子级列表
        childUl?.appendChild(sibling)
      }
    }

    // 将当前项移动到上层列表
    const nextElementSibling = parentLi.nextElementSibling
    if (nextElementSibling) {
      parentLi.parentElement?.insertBefore(li, nextElementSibling)
    } else {
      parentLi.parentElement?.appendChild(li)
    }

    // 清理空列表
    if (parentUl && parentUl.children.length < 1) {
      parentLi.removeChild(parentUl)
    }
  }
}

// 渲染 HTML
const renderHtml = async (markdown: string) => {
  await nextTick()
  if (editor.value) {
    // 如果是第一次输入且内容为空，直接插入文本
    if (isFirstInput.value && !markdown.trim()) {
      editor.value.innerHTML = '' // 清空内容
      isFirstInput.value = false
      return
    }

    // 保存当前光标位置
    saveSelection()

    // 更新内容
    htmlContent.value = getHtml(markdown)
    editor.value.innerHTML = htmlContent.value

    // // 使用 MutationObserver 监听 DOM 变化
    // const observer = new MutationObserver(() => {
    // 	// 恢复光标位置
    // 	restoreSelection();
    // 	observer.disconnect(); // 停止监听
    // });

    // // 监听 editor 的子节点变化
    // observer.observe(editor.value, { childList: true, subtree: true });
  }
}

// 获取 Markdown
const getMarkdown = (): string => {
  return turndownService.turndown(editor.value?.innerHTML || '')
}

// 获取 HTML
const getHtml = (markdown: string): string => {
  markdown = markdown.replace(/(?<!\n)```/g, '\n```')
  return DOMPurify.sanitize(String(marked(markdown)), { FORBID_TAGS: FORBID_TAGS }) // 清理危险 HTML;
}

// 插入空行
const insertEmptyLine = () => {
  document.execCommand('insertHTML', false, '<p><br></p>')
  emit('update:modelValue', getMarkdown())
  pushHistory(getMarkdown())
}

// 保存选区
const saveSelection = () => {
  const selection = window.getSelection()
  if (selection && selection.rangeCount > 0) {
    lastSelectionRange.value = selection.getRangeAt(0)
    lastFocusNode.value = selection.focusNode
  }
}

// 恢复选区
const restoreSelection = () => {
  if (lastSelectionRange.value && editor.value) {
    const selection = window.getSelection()
    if (selection) {
      lastFocusNode.value = selection.focusNode
      selection.removeAllRanges()
      selection.addRange(lastSelectionRange.value)
      editor.value.focus()
    }
  }
}

// 获取元素路径
const getElementPath = (element: HTMLElement) => {
  const path: string[] = []
  while (element && element !== editor.value) {
    let selector = element.nodeName.toLowerCase()
    if (element.nodeType === Node.TEXT_NODE) {
      // 文本节点
      const textNodes = Array.from(element.parentNode?.childNodes || []).filter(
        (node) => node.nodeType === Node.TEXT_NODE,
      )
      selector = `${selector}=${textNodes.indexOf(element)}`
    } else if (element.id) {
      // 优先使用 ID 选择器
      selector = `#${element.id}`
    } else {
      // 计算元素在父节点中的位置
      const parent = element.parentNode
      if (parent) {
        // 获取所有同级元素
        const siblings = parent.children
        const index = Array.from(siblings).indexOf(element)
        if (index != -1) {
          selector += `:nth-child(${index + 1})` // nth-child 从 1 开始
        }
      }
    }
    path.unshift(selector) // 向前插入以保持从根到目标的顺序
    element = element.parentNode as HTMLElement // 向上遍历
  }
  return path.join(' > ')
}

// 通过路径获取元素
const getElementByPath = (path: string) => {
  const selector = path.replace(/\s\>\s#text=\d+/, '') // 移除文本节点索引
  let element: HTMLElement | null
  // 元素节点
  element = editor.value?.querySelector(selector) as HTMLElement

  if (element && /#text=\d+/.test(path)) {
    const index = Number(path.match(/#text=(\d+)/)?.[1]) // 获取文本节点索引
    // 文本节点
    const textNodes = Array.from(element?.childNodes || []).filter(
      (node) => node.nodeType === Node.TEXT_NODE,
    )
    if (index != -1 && textNodes[index]) element = textNodes[index] as HTMLElement
  }
  return element
}

// 插入标题
const insertHeader = (level: number) => {
  restoreSelection()
  document.execCommand('formatBlock', false, `h${level}`)
  saveSelection()
}

// 插入加粗
const insertBold = () => {
  document.execCommand('bold', false)
  saveSelection()
}

// 插入斜体
const insertItalic = () => {
  document.execCommand('italic', false)
  saveSelection()
}

// 插入删除线
const insertStrikethrough = () => {
  const selection = window.getSelection()
  if (!selection || selection.rangeCount === 0) return

  const range = selection.getRangeAt(0).cloneRange()
  if (range.collapsed) return

  // 检查当前选区是否在删除线标签内
  const isInDel = bubbleFindNodeByFocusNode((node) => node.nodeName === 'DEL')

  if (isInDel) {
    // 如果在删除线标签内，取消删除线
    const parent = isInDel.parentNode
    if (parent) {
      const insertNodes: Node[] = []

      // 将删除线内的内容提升到父节点
      while (isInDel.firstChild) {
        const node = parent.insertBefore(isInDel.firstChild, isInDel)
        insertNodes.push(node)
      }
      parent.removeChild(isInDel)

      // 恢复选区
      const newRange = document.createRange()
      newRange.setStart(insertNodes[0], 0)
      newRange.setEnd(
        insertNodes[insertNodes.length - 1],
        insertNodes[insertNodes.length - 1].textContent?.length || 0,
      )
      selection.removeAllRanges()
      selection.addRange(newRange)
    }
  } else {
    try {
      // 尝试直接包裹选区内容
      const delElement = document.createElement('del')
      range.surroundContents(delElement)

      // 重新选择内容以便后续编辑
      range.selectNodeContents(delElement)
      selection.removeAllRanges()
      selection.addRange(range)
    } catch (e) {
      // 如果直接包裹失败（跨越节点），使用更安全的方式
      const fragment = document.createDocumentFragment()
      const delElement = document.createElement('del')

      // 将选区内容移动到临时容器
      const contents = range.extractContents()
      fragment.appendChild(contents)

      // 将内容放入删除线标签
      delElement.appendChild(fragment)

      // 在选区开始位置插入删除线标签
      range.insertNode(delElement)

      // 恢复选区
      const newRange = document.createRange()
      newRange.setStart(delElement, 0)
      newRange.setEnd(delElement, delElement.childNodes.length)
      selection.removeAllRanges()
      selection.addRange(newRange)
    }
  }

  saveSelection()
}

// 插入链接
const insertLink = () => {
  const url = prompt('请输入链接地址:')
  if (url) {
    document.execCommand('createLink', false, url)
    saveSelection()
  }
}

// 插入图片
const insertImage = () => {
  const url = prompt('请输入图片地址:')
  if (url) {
    document.execCommand('insertImage', false, url)
    saveSelection()
  }
}

// 插入代码
const insertCode = () => {
  // document.execCommand('formatBlock', false, 'pre');
  document.execCommand(
    'insertHTML',
    false,
    '<div class="v-md-pre-wrapper-javascript"><pre><code><br></code></pre></div>',
  )
  saveSelection()
}

// 插入无序列表
const insertUnorderedList = () => {
  document.execCommand('insertUnorderedList', false)
  saveSelection()
}

// 插入有序列表
const insertOrderedList = () => {
  document.execCommand('insertOrderedList', false)
  saveSelection()
}

// 插入引用
const insertBlockquote = () => {
  // document.execCommand('formatBlock', false, 'blockquote');
  // saveSelection();

  restoreSelection()
  const selection = window.getSelection()
  if (!selection || selection.rangeCount === 0) return

  let blockElement = selection.focusNode as HTMLElement

  // 检查当前是否为 blockquote
  let isInBlockquote = blockElement.tagName === 'BLOCKQUOTE'

  // 向上查找最近的块级元素
  while (blockElement && !isInBlockquote) {
    blockElement = blockElement.parentElement!
    isInBlockquote = blockElement?.tagName === 'BLOCKQUOTE'
  }

  // 切换格式
  if (isInBlockquote) {
    // 取消引用：转换为 p 标签
    const p = document.createElement('p')
    p.append(...Array.from(blockElement.childNodes))
    blockElement.parentNode?.replaceChild(p, blockElement)
  } else {
    // 创建引用
    document.execCommand('formatBlock', false, 'blockquote')
  }
  saveSelection()

  // 触发更新
  emit('update:modelValue', getMarkdown())
  pushHistory(getMarkdown())
}

/**
 * 恢复编辑器选区通过编辑记录
 * @param item 编辑记录
 */
const restoreSelectionByEditHistory = (item: EditHistoryItem) => {
  if (item.selection && editor.value) {
    const selection = window.getSelection()
    if (selection && item.selection.range) {
      const startContainer = getElementByPath(item.selection.range.startContainer) as Node
      const endContainer = getElementByPath(item.selection.range.endContainer) as Node
      const startOffset = item.selection.range.startOffset
      const endOffset = item.selection.range.endOffset
      const range = document.createRange()
      if (startContainer && endContainer) {
        range.setStart(startContainer, startOffset)
        range.setEnd(endContainer, endOffset)
        selection.removeAllRanges()
        selection.addRange(range)
        editor.value?.focus()
      }
    }
  }
}

// 编辑记录
const pushHistory = (markdown: string) => {
  const html = editor.value?.innerHTML || getHtml(markdown) || ''
  const selection = window.getSelection()
  const selectionRecord: EditHistoryItem['selection'] = {
    range: undefined,
    focusNodePath: undefined,
  }
  if (selection && selection.rangeCount > 0) {
    const range = selection.getRangeAt(0)
    selectionRecord.range = {
      startContainer: getElementPath(range.startContainer as HTMLElement),
      endContainer: getElementPath(range.endContainer as HTMLElement),
      startOffset: range.startOffset,
      endOffset: range.endOffset,
    }
    selectionRecord.focusNodePath = getElementPath(selection.focusNode as HTMLElement)
  }
  editHistory.value = editHistory.value.slice(0, historyIndex.value + 1)
  editHistory.value.push({ html, markdown, selection: selectionRecord })
  historyIndex.value++
}

// 撤销
const undo = async () => {
  if (historyIndex.value > 0) {
    historyIndex.value--
    const item = editHistory.value[historyIndex.value]
    // await renderHtml(item.markdown);
    editor.value!.innerHTML = item.html
    await nextTick()
    restoreSelectionByEditHistory(item)
  }
}

// 恢复
const redo = async () => {
  if (historyIndex.value < editHistory.value.length - 1) {
    historyIndex.value++
    const item = editHistory.value[historyIndex.value]
    // await renderHtml(item.markdown);
    editor.value!.innerHTML = item.html
    await nextTick()
    restoreSelectionByEditHistory(item)
  }
}

// 是否可以撤销
const canUndo = computed(() => historyIndex.value > 0)

// 是否可以恢复
const canRedo = computed(() => historyIndex.value < editHistory.value.length - 1)

const focus = () => {
  editor.value?.focus()

  // 创建选区并定位到末尾
  if (!lastSelectionRange.value && window.getSelection) {
    const range = document.createRange()
    range.selectNodeContents(editor.value!)
    range.collapse(false)
    const sel = window.getSelection()!
    sel.removeAllRanges()
    sel.addRange(range)
    saveSelection()
  }
}

/**
 * 插入内容
 * @param newValue 新的 Markdown 内容
 */
const insert = (newValue: string) => {
  if (newValue && newValue !== getMarkdown()) {
    if (lastFocusNode.value) restoreSelection()
    const content = getHtml(newValue)
    document.execCommand('insertHTML', false, content)
    saveSelection()

    nextTick(() => {
      const selection = window.getSelection()
      if (selection && selection.rangeCount > 0) {
        restoreSelection()

        // 滚动到光标处
        const rect = selection.getRangeAt(0)?.getBoundingClientRect()
        const editorRect = editor.value?.getBoundingClientRect()
        editor.value?.scrollTo({
          top:
            rect.top -
            (editorRect?.top ?? 0) -
            editor.value?.clientHeight / 3 +
            editor.value?.scrollTop,
          behavior: 'instant',
        })
      }
    })
  }
}

// 重新计算工具栏高度
const resize = async () => {
  await nextTick()
  while (!toolbar.value) {
    await new Promise((resolve) => setTimeout(resolve, 100))
  }
  toolbarHeight.value = toolbar.value?.offsetHeight
}

// 处理输入事件
const onInput = () => {
  const markdown = getMarkdown()
  if (editor.value) {
    const text = editor.value.innerHTML
    if (text.length < 1 || text === '<p><br></p>') {
      editor.value.innerHTML = '' // 清空内容
    }
  }
  if (markdown.trim() === '') {
    // 如果内容为空，直接设置为空字符串
    emit('update:modelValue', '')
    pushHistory('')
  } else {
    emit('update:modelValue', markdown)
    pushHistory(markdown)
  }
}

/**
 * 处理粘贴事件
 * @param event 粘贴事件对象
 */
const handlePaste = async (event: ClipboardEvent) => {
  event.preventDefault()

  let text = event.clipboardData?.getData('text/plain') || ''
  const html = event.clipboardData?.getData('text/html') || ''

  // 如果存在 HTML 内容，优先转换为 Markdown
  if (html) {
    text = turndownService.turndown(html)
  }

  if (!text.trim()) {
    // 如果粘贴内容为空，直接返回
    return
  }

  const parsedHtml = getHtml(text)

  // 插入 HTML 到编辑器
  const selection = window.getSelection()
  if (!selection || !editor.value) return

  if (!selection.isCollapsed) {
    document.execCommand('delete')
  }

  document.execCommand('insertHTML', false, parsedHtml)
  emit('update:modelValue', getMarkdown())
}

// 处理键盘事件
const handleKeyDown = (event: KeyboardEvent) => {
  // 处理回车事件
  if (event.key === 'Enter') {
    const el = bubbleFindNodeByFocusNode((node) => node.nodeName === 'BLOCKQUOTE') as HTMLElement
    if (el) {
      event.preventDefault()
      const nextElementSibling = el.nextElementSibling
      const newNode = document.createElement('p')
      newNode.appendChild(document.createElement('br'))
      if (nextElementSibling) {
        el.parentElement?.insertBefore(newNode, nextElementSibling)
      } else {
        el.parentElement?.appendChild(newNode)
      }

      const selection = window.getSelection()
      if (selection) {
        const range = document.createRange()
        range.selectNodeContents(newNode)
        selection.removeAllRanges()
        selection.addRange(range)
      }

      // 在第二行插入空行
      insertEmptyLine()
    }
  }

  // 处理删除事件
  if (event.key === 'Backspace' || event.key === 'Delete') {
    if (editor.value && isContentFullySelected(editor.value)) {
      event.preventDefault()
      editor.value.innerHTML = ''
      insertEmptyLine()
    }
  }

  // 处理缩进缩减事件
  if (event.key === 'Tab') {
    event.preventDefault()
    const li = bubbleFindNodeByFocusNode((node) => node.nodeName === 'LI') as HTMLElement

    if (li) {
      const selection = window.getSelection()
      const oldRange = selection?.getRangeAt(0)
      const startContainer = oldRange?.startContainer || li
      const endContainer = oldRange?.endContainer || li
      const startOffset = oldRange?.startOffset || 0
      const endOffset = oldRange?.endOffset || 0

      if (event.shiftKey) {
        unindentList(li)
      } else {
        indentList(li)
      }
      if (selection) {
        const range = document.createRange()
        range.setStart(startContainer, startOffset)
        range.setEnd(endContainer, endOffset)
        selection.removeAllRanges()
        selection.addRange(range)
      }
    } else {
      if (event.shiftKey) {
        const selection = window.getSelection()
        const focusNode = selection?.focusNode as HTMLElement
        const text = focusNode?.textContent || ''

        if (selection && /\s/.test(text)) {
          const range = document.createRange()
          const cursorPosition = selection?.focusOffset || 0
          // 删除光标左侧最近的空格字符
          let newText = text.slice(0, cursorPosition).replace(/\s{1}$/, '')
          newText += text.slice(cursorPosition)
          focusNode.textContent = newText
          range.setStart(focusNode, cursorPosition - 1)
          range.setEnd(focusNode, cursorPosition - 1) // 光标位置减1，因为删除了一个字符
          selection.removeAllRanges()
          selection.addRange(range)
        }
      } else {
        // 插入空格缩进
        document.execCommand('insertHTML', false, '&emsp;') // 插入一个空格
      }
    }
    pushHistory(getMarkdown())
  }

  // 处理恢复事件
  if (event.key === 'y' && event.ctrlKey) {
    event.preventDefault()
    if (canRedo.value) {
      redo()
    }
  }

  // 处理撤销事件
  if (event.key === 'z' && event.ctrlKey) {
    event.preventDefault()
    if (canUndo.value) {
      undo()
    }
  }
}

/**
 * 处理保存事件
 */
const handleSave = () => {
  emit('save', getMarkdown())
}

const handleGlobalKeyDown = (event: KeyboardEvent) => {
  // 按下 Esc 键退出全屏
  if (event.key === 'Escape' && isFullscreen.value) {
    event.preventDefault()
    isFullscreen.value = false
  }
}

const toggleDropdown = () => {
  dropdownVisible.value = !dropdownVisible.value
}

const handleGlobalClick = (event: MouseEvent) => {
  if (dropdownVisible.value) {
    const target = event.target as HTMLElement
    if (!target.closest('.dropdown')) {
      dropdownVisible.value = false
    }
  }
}

// 监听 modelValue 变化
watch(
  () => props.modelValue,
  (newValue) => {
    if (newValue !== getMarkdown()) {
      renderHtml(newValue)
      pushHistory(newValue) // 更新历史记录
    }
  },
)

// 初始化
onMounted(() => {
  renderHtml(props.modelValue)
  pushHistory(props.modelValue)
  resize() // 初始化时调用 resize

  document.addEventListener('keydown', handleGlobalKeyDown, false)
  document.addEventListener('click', handleGlobalClick, false)
})

onUnmounted(() => {
  document.removeEventListener('keydown', handleGlobalKeyDown, false)
  document.removeEventListener('click', handleGlobalClick, false)
})

defineExpose({
  focus,
  insert,
  resize,
})
</script>

<style lang="scss" scoped>
:global(.toolbar-popper) {
  position: fixed !important;
  z-index: 9999 !important;
}
:global(.el-popper.is-grey) {
  padding: 1px 8px;
  color: #fff;
  background-color: #666;
}
:global(.el-popper.is-grey .el-popper__arrow) {
  top: -3px;
}
:global(.el-popper.is-grey .el-popper__arrow::before) {
  width: 6px;
  height: 6px;
  background: #666;
  right: 0;
}

.markdown-editor {
  font-size: 16px;
  overflow: hidden;
  background-color: #ffffff;
}

.toolbar {
  padding: 6px;
  border-bottom: 1px solid #ddd;
  display: flex;
  align-items: center;
  gap: 4px;
}

.tooltip {
  position: relative;
  display: inline-block;
  font-size: 14px;
}

.tooltip[data-tooltip]:hover:before {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 4px solid transparent;
  border-top: 0;
  border-bottom-color: #666;
}

.tooltip[data-tooltip]:hover:after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%);
  background: #666;
  color: #fff;
  padding: 4px 8px;
  border-radius: 4px;
  white-space: nowrap;
}

.tooltip button {
  padding: 4px 8px;
  height: 26px;
  line-height: 26px;
  background: none;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.tooltip button:hover {
  background: #f5f5f5;
}

.dropdown {
  position: relative;
}

.dropdown-menu {
  padding: 0;
  margin: 0;
  border: 1px solid #ddd;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  list-style: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  color: #444;
  background: #fff;
}

.dropdown-menu li {
  padding: 8px 15px;
  white-space: nowrap;
  cursor: pointer;
}

.dropdown-menu li:hover {
  font-weight: bold;
  background: #f5f5f5;
}

.caret {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid #666;
  display: inline-block;
  vertical-align: super;
}

.editor {
  padding: 10px;
  outline: none;
  overflow-y: auto; /* 添加滚动条 */
  overflow-x: hidden;
}

.editor:empty:after {
  line-height: 1;
  content: attr(placeholder);
  color: #999;
  pointer-events: none;
}

.editor--fullscreen {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 99;
}

.icon {
  width: 16px;
  height: 16px;
  stroke: #444444;
  fill: #444444;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

[data-v-md-anchor] {
  cursor: pointer;
}
:deep(.vuepress-markdown-body) code[class*='v-md-prism-'],
:deep(.vuepress-markdown-body) pre[class*='v-md-prism-'] {
  color: #ccc;
  font-size: 1em;
  font-family:
    Consolas,
    Monaco,
    Andale Mono,
    Ubuntu Mono,
    monospace;
  line-height: 1.5;
  white-space: pre;
  text-align: left;
  word-wrap: normal;
  word-break: normal;
  word-spacing: normal;
  -webkit-hyphens: none;
  hyphens: none;
  background: none;
}
:deep(.vuepress-markdown-body) > :first-child,
:deep(.vuepress-markdown-body) > div[data-v-md-line]:first-child > :first-child {
  margin-top: 0 !important;
}
:deep(.vuepress-markdown-body) > :last-child,
:deep(.vuepress-markdown-body) > div[data-v-md-line]:last-child > :last-child {
  margin-bottom: 0 !important;
}
:deep(.vuepress-markdown-body) pre[class*='v-md-prism-'] {
  margin: 0.5em 0;
  padding: 1em;
  overflow: auto;
}
:deep(.vuepress-markdown-body) :not(pre) > code[class*='v-md-prism-'],
:deep(.vuepress-markdown-body) pre[class*='v-md-prism-'] {
  background: #2d2d2d;
}
:deep(.vuepress-markdown-body) :not(pre) > code[class*='v-md-prism-'] {
  padding: 0.1em;
  white-space: normal;
  border-radius: 0.3em;
}
:deep(.vuepress-markdown-body) .token.block-comment,
:deep(.vuepress-markdown-body) .token.cdata,
:deep(.vuepress-markdown-body) .token.comment,
:deep(.vuepress-markdown-body) .token.doctype,
:deep(.vuepress-markdown-body) .token.prolog {
  color: #999;
}
:deep(.vuepress-markdown-body) .token.punctuation {
  color: #ccc;
}
:deep(.vuepress-markdown-body) .token.attr-name,
:deep(.vuepress-markdown-body) .token.deleted,
:deep(.vuepress-markdown-body) .token.namespace,
:deep(.vuepress-markdown-body) .token.tag {
  color: #e2777a;
}
:deep(.vuepress-markdown-body) .token.function-name {
  color: #6196cc;
}
:deep(.vuepress-markdown-body) .token.boolean,
:deep(.vuepress-markdown-body) .token.function,
:deep(.vuepress-markdown-body) .token.number {
  color: #f08d49;
}
:deep(.vuepress-markdown-body) .token.class-name,
:deep(.vuepress-markdown-body) .token.constant,
:deep(.vuepress-markdown-body) .token.property,
:deep(.vuepress-markdown-body) .token.symbol {
  color: #f8c555;
}
:deep(.vuepress-markdown-body) .token.atrule,
:deep(.vuepress-markdown-body) .token.builtin,
:deep(.vuepress-markdown-body) .token.important,
:deep(.vuepress-markdown-body) .token.keyword,
:deep(.vuepress-markdown-body) .token.selector {
  color: #cc99cd;
}
:deep(.vuepress-markdown-body) .token.attr-value,
:deep(.vuepress-markdown-body) .token.char,
:deep(.vuepress-markdown-body) .token.regex,
:deep(.vuepress-markdown-body) .token.string,
:deep(.vuepress-markdown-body) .token.variable {
  color: #7ec699;
}
:deep(.vuepress-markdown-body) .token.entity,
:deep(.vuepress-markdown-body) .token.operator,
:deep(.vuepress-markdown-body) .token.url {
  color: #67cdcc;
}
:deep(.vuepress-markdown-body) .token.bold,
:deep(.vuepress-markdown-body) .token.important {
  font-weight: 700;
}
:deep(.vuepress-markdown-body) .token.italic {
  font-style: italic;
}
:deep(.vuepress-markdown-body) .token.entity {
  cursor: help;
}
:deep(.vuepress-markdown-body) .token.inserted {
  color: green;
}
:deep(.vuepress-markdown-body) code {
  margin: 0;
  padding: 0.25rem 0.5rem;
  color: #476582;
  font-size: 0.85em;
  background-color: rgba(27, 31, 35, 0.05);
  border-radius: 3px;
}
:deep(.vuepress-markdown-body) code .token.deleted {
  color: #ec5975;
}
:deep(.vuepress-markdown-body) code .token.inserted {
  color: #3eaf7c;
}
:deep(.vuepress-markdown-body) pre,
:deep(.vuepress-markdown-body) pre[class*='v-md-prism-'] {
  margin: 0.85rem 0;
  padding: 1.25rem 1.5rem;
  overflow: auto;
  line-height: 1.4;
  background-color: #282c34;
  border-radius: 6px;
}
:deep(.vuepress-markdown-body) pre[class*='v-md-prism-'] code,
:deep(.vuepress-markdown-body) pre code {
  padding: 0;
  color: #fff;
  background-color: initial;
  border-radius: 0;
}
:deep(.vuepress-markdown-body) div[class*='v-md-pre-wrapper-'] {
  position: relative;
  background-color: #282c34;
  border-radius: 6px;
}
:deep(.vuepress-markdown-body) div[class*='v-md-pre-wrapper-'] pre,
:deep(.vuepress-markdown-body) div[class*='v-md-pre-wrapper-'] pre[class*='v-md-prism-'] {
  position: relative;
  z-index: 1;
  background: transparent;
}
:deep(.vuepress-markdown-body) div[class*='v-md-pre-wrapper-']:before {
  position: absolute;
  top: 0.8em;
  right: 1em;
  z-index: 3;
  color: hsla(0, 0%, 100%, 0.4);
  font-size: 0.75rem;
}
:deep(.vuepress-markdown-body)
  div[class*='v-md-pre-wrapper-']:not(.line-numbers-mode)
  .line-numbers-wrapper {
  display: none;
}
:deep(.vuepress-markdown-body) div[class*='v-md-pre-wrapper-'].line-numbers-mode pre {
  padding-left: 4.5rem;
  vertical-align: middle;
}
:deep(.vuepress-markdown-body)
  div[class*='v-md-pre-wrapper-'].line-numbers-mode
  .line-numbers-wrapper {
  position: absolute;
  top: 0;
  width: 3.5rem;
  padding: 1.25rem 0;
  color: hsla(0, 0%, 100%, 0.3);
  line-height: 1.4;
  text-align: center;
}
:deep(.vuepress-markdown-body)
  div[class*='v-md-pre-wrapper-'].line-numbers-mode
  .line-numbers-wrapper
  br {
  -webkit-user-select: none;
  user-select: none;
}
:deep(.vuepress-markdown-body)
  div[class*='v-md-pre-wrapper-'].line-numbers-mode
  .line-numbers-wrapper
  .line-number {
  position: relative;
  z-index: 4;
  font-size: 0.85em;
  -webkit-user-select: none;
  user-select: none;
}
:deep(.vuepress-markdown-body) div[class*='v-md-pre-wrapper-'].line-numbers-mode:after {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 3.5rem;
  height: 100%;
  background-color: #282c34;
  border-right: 1px solid rgba(0, 0, 0, 0.66);
  border-radius: 6px 0 0 6px;
  content: '';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-js']:before {
  content: 'js';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-ts']:before {
  content: 'ts';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-html']:before {
  content: 'html';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-md']:before {
  content: 'md';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-vue']:before {
  content: 'vue';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-css']:before {
  content: 'css';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-sass']:before {
  content: 'sass';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-scss']:before {
  content: 'scss';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-less']:before {
  content: 'less';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-stylus']:before {
  content: 'stylus';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-go']:before {
  content: 'go';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-java']:before {
  content: 'java';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-c']:before {
  content: 'c';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-sh']:before {
  content: 'sh';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-yaml']:before {
  content: 'yaml';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-py']:before {
  content: 'py';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-docker']:before {
  content: 'docker';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-dockerfile']:before {
  content: 'dockerfile';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-makefile']:before {
  content: 'makefile';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-javascript']:before {
  content: 'js';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-typescript']:before {
  content: 'ts';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-markup']:before {
  content: 'html';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-markdown']:before {
  content: 'md';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-json']:before {
  content: 'json';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-ruby']:before {
  content: 'rb';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-python']:before {
  content: 'py';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-bash']:before {
  content: 'sh';
}
:deep(.vuepress-markdown-body) div[class~='v-md-pre-wrapper-php']:before {
  content: 'php';
}
:deep(.vuepress-markdown-body) .arrow {
  display: inline-block;
  width: 0;
  height: 0;
}
:deep(.vuepress-markdown-body) .arrow.up {
  border-bottom: 6px solid #ccc;
}
:deep(.vuepress-markdown-body) .arrow.down,
:deep(.vuepress-markdown-body) .arrow.up {
  border-right: 4px solid transparent;
  border-left: 4px solid transparent;
}
:deep(.vuepress-markdown-body) .arrow.down {
  border-top: 6px solid #ccc;
}
:deep(.vuepress-markdown-body) .arrow.right {
  border-left: 6px solid #ccc;
}
:deep(.vuepress-markdown-body) .arrow.left,
:deep(.vuepress-markdown-body) .arrow.right {
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
}
:deep(.vuepress-markdown-body) .arrow.left {
  border-right: 6px solid #ccc;
}
/* :deep(.vuepress-markdown-body):not(.custom) {
	padding: 2rem 2.5rem;
}
@media (max-width: 959px) {
	:deep(.vuepress-markdown-body):not(.custom) {
		padding: 2rem;
	}
}
@media (max-width: 419px) {
	:deep(.vuepress-markdown-body):not(.custom) {
		padding: 1.5rem;
	}
} */
:deep(.vuepress-markdown-body) .table-of-contents .badge {
  vertical-align: middle;
}
:deep(.vuepress-markdown-body) {
  color: #2c3e50;
  font-size: 16px;
  font-family:
    -apple-system,
    BlinkMacSystemFont,
    Segoe UI,
    Roboto,
    Oxygen,
    Ubuntu,
    Cantarell,
    Fira Sans,
    Droid Sans,
    Helvetica Neue,
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background-color: #fff;
}
:deep(.vuepress-markdown-body):not(.custom) a:hover {
  text-decoration: underline;
}
:deep(.vuepress-markdown-body):not(.custom) p.demo {
  padding: 1rem 1.5rem;
  border: 1px solid #ddd;
  border-radius: 4px;
}
:deep(.vuepress-markdown-body):not(.custom) img {
  max-width: 100%;
}
:deep(.vuepress-markdown-body).custom {
  margin: 0;
  padding: 0;
}
:deep(.vuepress-markdown-body).custom img {
  max-width: 100%;
}
:deep(.vuepress-markdown-body) a {
  font-weight: 500;
  text-decoration: none;
}
:deep(.vuepress-markdown-body) a,
:deep(.vuepress-markdown-body) p a code {
  color: #3eaf7c;
}
:deep(.vuepress-markdown-body) p a code {
  font-weight: 400;
}
:deep(.vuepress-markdown-body) kbd {
  padding: 0 0.15em;
  background: #eee;
  border: 0.15rem solid #ddd;
  border-bottom: 0.25rem solid #ddd;
  border-radius: 0.15rem;
}
:deep(.vuepress-markdown-body) blockquote {
  margin: 1rem 0;
  padding: 0.25rem 0 0.25rem 1rem;
  min-height: 30px;
  color: #999;
  font-size: 1rem;
  border-left: 0.2rem solid #dfe2e5;
}
:deep(.vuepress-markdown-body) blockquote > p {
  margin: 0;
}
:deep(.vuepress-markdown-body) ol,
:deep(.vuepress-markdown-body) ul {
  margin: 1em 0;
  padding-left: 1.2em;
}
:deep(.vuepress-markdown-body) strong {
  font-weight: 600;
}
:deep(.vuepress-markdown-body) h1,
:deep(.vuepress-markdown-body) h2,
:deep(.vuepress-markdown-body) h3,
:deep(.vuepress-markdown-body) h4,
:deep(.vuepress-markdown-body) h5,
:deep(.vuepress-markdown-body) h6 {
  font-weight: 600;
  line-height: 1.25;
}
:deep(.vuepress-markdown-body) h1 {
  margin: 0.67em 0;
  font-size: 2.2rem;
}
:deep(.vuepress-markdown-body) h2 {
  margin: 0.83em 0;
  padding-bottom: 0.3rem;
  font-size: 1.65rem;
  border-bottom: 1px solid #eaecef;
}
:deep(.vuepress-markdown-body) h3 {
  margin: 1em 0;
  font-size: 1.35rem;
}
:deep(.vuepress-markdown-body) h4 {
  margin: 1.33em 0;
}
:deep(.vuepress-markdown-body) h5 {
  margin: 1.67em 0;
}
:deep(.vuepress-markdown-body) h6 {
  margin: 2.33em 0;
}
:deep(.vuepress-markdown-body) em,
:deep(.vuepress-markdown-body) i {
  font-style: italic;
}
:deep(.vuepress-markdown-body) ul {
  list-style-type: disc;
}
:deep(.vuepress-markdown-body) ol ul,
:deep(.vuepress-markdown-body) ul ul {
  list-style-type: circle;
}
:deep(.vuepress-markdown-body) ol ol ul,
:deep(.vuepress-markdown-body) ol ul ul,
:deep(.vuepress-markdown-body) ul ol ul,
:deep(.vuepress-markdown-body) ul ul ul {
  list-style-type: square;
}
:deep(.vuepress-markdown-body) ol {
  list-style-type: decimal;
}
:deep(.vuepress-markdown-body) .line-number,
:deep(.vuepress-markdown-body) code,
:deep(.vuepress-markdown-body) kbd {
  font-family:
    source-code-pro,
    Menlo,
    Monaco,
    Consolas,
    Courier New,
    monospace;
}
:deep(.vuepress-markdown-body) ol,
:deep(.vuepress-markdown-body) p,
:deep(.vuepress-markdown-body) ul {
  line-height: 1.7;
}
:deep(.vuepress-markdown-body) hr {
  border: 0;
  border-top: 1px solid #eaecef;
}
:deep(.vuepress-markdown-body) table {
  display: block;
  margin: 1rem 0;
  overflow-x: auto;
  border-collapse: collapse;
}
:deep(.vuepress-markdown-body) tr {
  border-top: 1px solid #dfe2e5;
}
:deep(.vuepress-markdown-body) tr:nth-child(2n) {
  background-color: #f6f8fa;
}
:deep(.vuepress-markdown-body) td,
:deep(.vuepress-markdown-body) th {
  padding: 0.6em 1em;
  border: 1px solid #dfe2e5;
}
:deep(.vuepress-markdown-body) th {
  white-space: nowrap;
}
:deep(.vuepress-markdown-body) .v-md-svg-outbound {
  position: relative;
  top: -1px;
  display: inline-block;
  color: #aaa;
  vertical-align: middle;
}
@media (max-width: 419px) {
  :deep(.vuepress-markdown-body) h1 {
    font-size: 1.9rem;
  }
  :deep(.vuepress-markdown-body) div[class*='v-md-pre-wrapper-'] {
    margin: 0.85rem -1.5rem;
    border-radius: 0;
  }
}
</style>
